package com.ushine.web.config;


import com.ushine.web.base.service.impl.SecurityUserServiceImpl;
import com.ushine.web.security.handler.*;
import com.ushine.web.security.jwt.JwtAuthenticationFilter;
import com.ushine.web.security.jwt.JwtAuthenticationProvider;
import com.ushine.web.security.utils.RsaKeyConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * @Author: yls
 * @Date: 2021/08/15 21:27
 * @Description: 1、默认的登录用户是【user】，登录，密码是一个随机生产的【UUID】，在项目启动的日志中可以看到
 * 2、可以配置很多
 * 注意：token过期的异常：ExpiredJwtException
 * @Version 1.0
 */
@Configuration
public class BaseSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private FightAuthenticationSuccessHandler fightAuthenticationSuccessHandler;

    @Resource
    private FightAuthenticationFailureHandler fightAuthenticationFailureHandler;

    @Resource
    private FightLogoutHandler fightLogoutHandler;

    @Resource
    private FightLogoutSuccessHandler fightLogoutSuccessHandler;

    @Resource
    private SecurityUserServiceImpl fightUserService;

    @Resource
    private RsaKeyConfig rsaKeyConfig;

    @Resource
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Resource
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Resource
    private RestAuthenticationAccessDeniedHandler accessDeniedHandler;


    /**
     * 功能描述:
     * 〈密码的生成策略，其中的参数【strength】，默认为10,值越大消耗的资源越高〉
     *
     * @return : org.springframework.security.crypto.password.PasswordEncoder
     * @author : yls
     * @date : 2021/8/18 15:43
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 功能描述:
     * 〈 整合数据库，查询用户和角色 〉
     *
     * @param auth 凭证信息
     * @return : void
     * @author : yls
     * @date : 2021/8/18 15:42
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(fightUserService).passwordEncoder(passwordEncoder());
        auth.authenticationProvider(new JwtAuthenticationProvider(fightUserService,rsaKeyConfig));
    }

    /**
     * 功能描述:
     * 〈 静态资源过滤：有些静态资源不需要经过【Spring Security】过滤器 〉
     *
     * @param web 静态资源配置
     * @return : void
     * @author : yls
     * @date : 2021/08/17 22:15
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/css/**",
                "/js/**",
                "/webjars/**",
                "/login.html",

                "/swagger-ui/**",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/v3/api-docs",
                "/favicon.ico",
                "/webjars/**",
                "/doc.html/**");
    }

    /**
     * 功能描述:
     * 〈 登录、注销相关的配置信息 〉
     *
     * @param http http相关的安全配置
     * @return : void
     * @author : yls
     * @date : 2021/08/17 22:14
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 登录【login】允许匿名访问
                .antMatchers("/login").anonymous()
//                .anyRequest().authenticated();
                .anyRequest().permitAll();
        // 功能权限通过表达式控制，和请求的 url 绑定
//                .anyRequest().access("@authorityServiceImpl.hasPermission(request,authentication)");

        // 登录
        http.formLogin()
                .successHandler(fightAuthenticationSuccessHandler)
                .failureHandler(fightAuthenticationFailureHandler);

        // 注销
        http.logout()
                // 清除【session】
                .invalidateHttpSession(true)
                .addLogoutHandler(fightLogoutHandler)
                // 清除认证信息
                .clearAuthentication(true)
                .logoutSuccessHandler(fightLogoutSuccessHandler);

        // csrf的相关配置
        http.csrf()
                .disable();

        // 禁止请求头缓存
        http.headers().cacheControl();

        http.exceptionHandling()
                // 无权限访问
                .accessDeniedHandler(accessDeniedHandler)
                // 禁止匿名访问
                .authenticationEntryPoint(jwtAuthenticationEntryPoint);

        // 会话管理：单点登录，如果第二次登录，将会强行将第一登录的用户踢下线
        http.sessionManagement()
                // 设置Session的创建策略为：Spring Security永不创建HttpSession 不使用HttpSession
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 在用户名密码凭证验证之前添加【token】校验
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);


    }

    /**
     * 功能描述:
     * 〈跨域的相关配置〉
     *
     * @return : org.springframework.web.cors.CorsConfigurationSource
     * @author : yls
     * @date : 2021/8/18 13:34
     */
    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许跨域的请求
        corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
        // 允许跨域的方法
        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));
        corsConfiguration.applyPermitDefaultValues();
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
        // 允许跨域的资源
        configurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return configurationSource;
    }

}
